function [A] = structarrayfield2array(s, fieldName)
%STRUCTARRAYFIELD2ARRAY 将结构体数组中指定的域生成数组
%
% Input Arguments
% # s: 结构体数组，尺寸为szStruct
% # fieldName: 字符串，结构体数组的域名，该域的值为数值数组（尺寸为szField）或者结构体标量
%
% Output Arguments
% # A: 当域为数值数组时，A为尺寸为[szField szStruct]的数值数组，由s中域名为fieldName的数组按其在s中的排列构成的数组
%      当域为结构体标量时，A为尺寸为[szStruct]的结构体数组，由s中域名为fieldName的结构体按其在s中的排列构成的结构体数组
%
% Assumptions and Limitations
% # 假设MATLAB使用列优先索引
% # 假设MATLAB沿第2维合并结构数组的域
% # cannot generate array for fields with different element sizes (will return empty array)

% NOTE: 本函数使用线性索引

coder.extrinsic('warning');

szStruct = size(s);
firstNonEmptyInd = 0;
for i=1:prod(szStruct)
    if ~isempty(s(i).(fieldName))
        firstNonEmptyInd = i;
        break;
    end
end
if firstNonEmptyInd == 0
    warning('structarrayfield2array:emptyField', ['field ' fieldName ' of s is all empty']);
    A = [];
else
    szField = size(s(firstNonEmptyInd).(fieldName));
    sz = [szField szStruct];
    nFieldElem = prod(szField);
    if isstruct(s(firstNonEmptyInd).(fieldName))
        if nFieldElem == 1
            A = repmat(structcpy_fieldnames(s(firstNonEmptyInd).(fieldName)), szStruct);
            for i=1:prod(szStruct)
                if ~isempty(s(i).(fieldName))
                    A(i) = s(i).(fieldName);
                end
            end
        else
            error('structarrayfield2array:fielderror', 'Field is a non-scalar struct.');
        end
    elseif isnumeric(s(firstNonEmptyInd).(fieldName)) || islogical(s(firstNonEmptyInd).(fieldName))
        if coder.target('MATLAB') % 当在MATLAB下运行时，直接访问结构数组的域以提高速度
            try
                A = [s.(fieldName)];
                if numel(A) ~= prod(sz)
                    warning('structarrayfield2array:emptyfield', ['Field ' fieldName ' of some element struct may be empty.']);
                end
                dimField = length(szField);
                if dimField > 2 % MATLAB默认沿第2维合并结构数组的域
                    order = [1 3:dimField 2];
                    A = permute(A, order);
                    A = reshape(A, [szField(1) szField(3:dimField) szField(2) szStruct]);
                    order = [1 dimField 2:(dimField-1) (dimField+1):length(sz)];
                    A = permute(A, order);
                else
                    A = reshape(A, sz);
                end
            catch ME
                warning(ME.identifier, '%s', ME.message); % TODO: 区分处理reshape异常
                warning('structarrayfield2array:combineerr', ['Field ' fieldName ' cannot be combined.']);
                A = [];
            end
        else
            if isnumeric(s(firstNonEmptyInd).(fieldName))
                A = NaN(sz);
            else
                A = false(sz);
            end
            for i=1:prod(szStruct)
                if ~isempty(s(i).(fieldName))
                    lb = (i-1)*nFieldElem + 1;
                    ub = i*nFieldElem;
                    A(lb:ub) = s(i).(fieldName);
                end
            end
        end
    else
        error('structarrayfield2array:fielderror', 'Field is neither a struct nor a numeric array.');
    end
end
end